//
// Fichier principale
//
// Voici le protocole utilisée :
// 1. RaspBL envoie au serveur (chargé d'envoyer le noyau) la chaine "RDY" pour
// dire qu'il est prêt
// 2. Le serveur répond en deux temps :
//	- d'abord la taille du noyau sur 32 octets
//	- ensuite le noyau en soit
//
// Une fois que le noyau est chargé, RaspBL lance celui-ci
//


#include "ok_led.h"
#include "asm_tools.h"
#include "uart.h"
#include "sys_timer.h"

//
// Adresse à laquelle on charge le noyau
#define KERNEL_ADDRESS	0x8000u
#define RASPBL_ADDRESS	0x10000000u
// Taille max du noyau
#define MAX_KERNEL_SIZE	(RASPBL_ADDRESS - KERNEL_ADDRESS)
// Temps à attendre avant de lancer le noyau
#define TIME_TO_WAIT	1000000u

//
// Fonciton erreur
// En cas d'erreur, on:
//	- envoie un message
//	- allume la led
//	- demande de redémarer
void error(const char* msg)
{
	uart_send(msg);
	uart_send("\nRedémarrer...");
	oled_turn_on();
	for ( ;; );
}

//
// Handler FIQ
// On est pas censer recevoir une FIQ.
// On evoie un message d'erreur est on allume la LED
void fiq_handler(void)
{
	error("FIQ exception received\n");
}

//
// Handler IRQ
void irq_handler(void)
{
	error("IRQ exception received\n");
}

//
// Handler Prefetch Abort
// On evoie un message d'erreur est on allume la LED
void prefetch_abort_handler(void)
{
	error("Prefetch abort exception received\n");
}

//
// Handler Data Abort
// On envoie un message d'erreur est on allume la LED
void data_abort_handler(void)
{
	error("Data abort exception received\n");
}

//
// Handler Undefined Instruction
// On envoie un message d'erreur est on allume la LED
void undefined_handler(void)
{
	error("Undefined exception received\n");
}

//
// Handler de SWI
// On est pas censé avoir de SWI
// On envoie un message d'erreur est on allume la LED
void swi_handler(void)
{
	error("SWI exception received\n");
}

//
// Not assigned
// on est JAMAIS censé arrivé là
// On envoie un message d'erreur est on allume la LED
void not_assigned_handler(void)
{
	error("Not assigned :-O\n");
}

//
// Charge le noyau
void load_kernel(void)
{
	unsigned char* add;
	unsigned char byte;
	unsigned int kern_size;
	unsigned int i;

	// Lecture de la taille du noyau
	kern_size = 0u;
	for (i = 0u; i < 32u; i += 8u)
	{
		while (uart_is_receive_fifo_empty() == 0);
		kern_size |= (uart_read_byte() << i);
	}

	// On vérifie la taille du noyau
	if (kern_size > MAX_KERNEL_SIZE)
	{
		error("Kernel trops grand !");
	}
	else if (kern_size == 0u)
	{
		error("La taille du kernel ne peut pas être nulle");
	} 

	// Chargement du noyau
	add = (unsigned char*)KERNEL_ADDRESS;
	do
	{
		// on attend que la pile se remplisse
		while (uart_is_receive_fifo_empty() == 0);
		// Si la pile est vide, on sort.
		byte = (unsigned char)uart_read_byte();
		*(add++) = byte;
	}
	while (--kern_size != 0u);
}

//
// Poursuit l'initialisation et attend le noyau.
int kmain(void)
{
	// Initialisation de la OK Led
	oled_init();

	// On initialise UART
	uart_init();

	// On active les interruptions FIQ et IRQ
	enableIRQandFIQ();

	// On fait clignoter deux fois pour dire que on est prêt
	// à recevoir le noyau.
	oled_flash(100000u, 2u);

	// On envoie un message pour dire qu'on est prêt
	uart_send("RDY");

	// On attend que le noyau arrive
	for ( ;; )
	{
		if (uart_is_receive_fifo_empty() != 0)
		{
			load_kernel();
			active_wait(TIME_TO_WAIT);
			jump_to(KERNEL_ADDRESS);
		}
	}

	return 0;
}
